/*
 *   Copyright (c) 2011 Hitachi Data Systems, Inc.
 *
 *   Permission is hereby granted to  this software and associated
 *   documentation files (the "Software"), subject to the terms and
 *   conditions of the Sample Source Code License (SSCL) delivered
 *   with this Software. If you do not agree to the terms and
 *   conditions of the SSCL,
 *
 *     (i)  you must close this file and delete all copies of the
 *          Software, and
 *     (ii) any permission to use the Software is expressly denied.
 *
 */


package com.hds.hcp.examples;

/***
 * This is a sample for how to utilize the HTTP Compression feature of HCP version 4.1. 
 * 
 * The feature allows applications to send (PUT) and receive (GET) over HTTP with gzip
 *   compression to save on Network Bandwidth.
 * 
 * When the HCP system receives (PUT) content that is indicated as compressed in the
 *   gzip format, it will decompress the HTTP pay load before storing it on the system.
 *
 * When the HCP system is requested to send (GET) content in in the gzip format,
 *   it will compress the content before sending it over HTTP to the requester.  
 *   The requester will than have to decompress the content before using it.
 *
 */

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;

import java.util.zip.GZIPInputStream;

import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.util.EntityUtils;

import com.hds.hcp.apihelpers.GZIPCompressedInputStream;
import com.hds.hcp.apihelpers.HCPUtils;

public class GZipIO {

	// Static strings for the file path info.
	private static final String sBaseFileName = "compress.txt";
	
	// Local member variables.
	private Boolean bIsInitialized = false;
	private String sEncodedUserName, sEncodedPassword;
	private String sHCPURLFilePath;
	
	private HttpClient mHttpClient;

	/**
	 * Initialize the object by setting up internal data and establishing the HTTP client connection.
	 * 
	 * This routine is called by the ReadFromHCP and WriteToHCP routines, so calling it by the
	 * consumer of this class is unnecessary.
	 */
	void initialize(String inNamespace, String inUsername, String inPassword) throws Exception {

		if (! bIsInitialized)  // Only initialize if we haven't already
		{
			// Build the URL File Path on HCP.
			sHCPURLFilePath = "https://" + inNamespace + "/rest/examples/" + sBaseFileName;
			
			// Encode both username and password for authentication string.
			sEncodedUserName = HCPUtils.toBase64Encoding(inUsername);
			sEncodedPassword = HCPUtils.toMD5Digest(inPassword);
			
			// Get an HTTP Client setup for sample usage.
			mHttpClient = HCPUtils.initHttpClient();
			
			bIsInitialized = true;
		}
	}
	
	/**
	 * This method performs an orderly shutdown of HTTP connection manager.
	 * 
	 */
	void shutdown() throws Exception {
		// Clean up open connections by shutting down connection manager.
		mHttpClient.getConnectionManager().shutdown();
	}
	
	/**
	 * This method performs a PUT of a file by using GZIP compression of the pay load.
	 * 
	 */
	void WriteToHCP() throws Exception {
		/*
		 * Setup the PUT request.
		 */
		HttpPut httpRequest = new HttpPut(sHCPURLFilePath);

		// Indicate that the content encoding is gzip.
		httpRequest.setHeader("Content-Encoding", "gzip");

		// Open an input stream to the disk file that will be sent to HCP.  This file
		// will be processed by the GZIPCompressedInputStream to produce gzip compressed
		// content when read by the Apache HTTP client.
		GZIPCompressedInputStream compressedInputFile
			= new GZIPCompressedInputStream(new FileInputStream(sBaseFileName + ".toHCP"));
			
		// Point the HttpRequest to the input stream.
		httpRequest.setEntity(new InputStreamEntity(compressedInputFile, -1));

		// Set HTTP Authorization Header with HCP credentials.
		httpRequest.setHeader(HCPUtils.HTTP_AUTH_HEADER, "HCP " + sEncodedUserName + ":" + sEncodedPassword);

		/*
		 * Now execute the PUT request.
		 */
		HttpResponse httpResponse = mHttpClient.execute(httpRequest);
	        
		/*
		 * Process the HTTP Response.
		 */
		
		// For debugging purposes, dump out the HTTP Response.
		HCPUtils.dumpHttpResponse(httpResponse);
	        
		// If the return code is anything BUT 200 range indicating success, we have to throw an exception.
		if (2 != (int)(httpResponse.getStatusLine().getStatusCode() / 100))
		{
			// Clean up after ourselves and release the HTTP connection to the connection manager.
			EntityUtils.consume(httpResponse.getEntity());
			
			throw new HttpResponseException(httpResponse.getStatusLine().getStatusCode(),
					"Unexpected status returned from " + httpRequest.getMethod() + " ("
					+ httpResponse.getStatusLine().getStatusCode() + ": " 
					+ httpResponse.getStatusLine().getReasonPhrase() + ")");
		}
		
		// Clean up after ourselves and release the HTTP connection to the connection manager.
		EntityUtils.consume(httpResponse.getEntity());
	}
	
	/**
	 * This method performs a GET of a file from HCP requesting it to be gzip compressed
	 * over the network.
	 * 
	 */
	void ReadFromHCP() throws Exception {
		/*
		 * Setup the GET request.
		 */
		HttpGet httpRequest = new HttpGet(sHCPURLFilePath);

		// Indicate that you wish HCP to encode the payload as gzip compressed.
		httpRequest.setHeader("Accept-Encoding", "gzip");
			
		// Set HTTP Authorization Header with HCP credentials.
		httpRequest.setHeader(HCPUtils.HTTP_AUTH_HEADER, "HCP " + sEncodedUserName + ":" + sEncodedPassword);

		/*
		 * Now execute the GET request.
		 */
		HttpResponse httpResponse = mHttpClient.execute(httpRequest);

		/*
		 * Process the HTTP Response.
		 */
		
		// For debugging purposes, dump out the HTTP Response.
		HCPUtils.dumpHttpResponse(httpResponse);
	        
		// If the return code is anything BUT 200 range indicating success, we have to throw an exception.
		if (2 != (int)(httpResponse.getStatusLine().getStatusCode() / 100))
		{
			// Clean up after ourselves and release the HTTP connection to the connection manager.
			EntityUtils.consume(httpResponse.getEntity());
			
			throw new HttpResponseException(httpResponse.getStatusLine().getStatusCode(),
					"Unexpected status returned from " + httpRequest.getMethod() + " ("
					+ httpResponse.getStatusLine().getStatusCode() + ": " 
					+ httpResponse.getStatusLine().getReasonPhrase() + ")");
	    }
	        
		/*
		 * Write the file to disk.
		 */

		//
		// The HCP Server might decide to not compress a file because it is too small,
		//   we need get the "Content-Encoding" HTTP Header to see.  Fail if there is
		//   too many.  (Never should happen)
		Header[] contentEncodingHdrs = httpResponse.getHeaders("Content-Encoding");
		if (1 > contentEncodingHdrs.length) {
			EntityUtils.consume(httpResponse.getEntity());
			
			throw new HttpResponseException(httpResponse.getStatusLine().getStatusCode(),
					"Unexpected number of Content-Encoding HTTP headers found in response");
		}
		
		// Setup output file.
		FileOutputStream outputFile = new FileOutputStream(sBaseFileName + ".fromHCP");
			
		// Assume it is not compressed.
		InputStream rawContent = null;
		
		// See if the content returned is compressed or not by examining if the 
		//  "Content-Encoding" HTTP header existing and is equal to "gzip".
		if (1 == contentEncodingHdrs.length && "gzip".equals(contentEncodingHdrs[0].getValue())) {
			// It was sent back as gzip, so use the GZIPInputStream to decompress.
			rawContent = new GZIPInputStream(httpResponse.getEntity().getContent());
		} else {
			// It was not sent back as gzip, so just read back uncompressed.
			rawContent = httpResponse.getEntity().getContent();
		}
		
		// Transfer the HTTP Response InputStream to the output file.
		byte partialRead[] = new byte[1024];
		int readSize = 0;
		while (-1 != (readSize = rawContent.read(partialRead))) {
			outputFile.write(partialRead, 0, readSize);
	    }
	
		outputFile.close();
			
		// Clean up after ourselves and release the HTTP connection to the connection manager.
		EntityUtils.consume(httpResponse.getEntity());
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {

		GZipIO myClass = new GZipIO();

		if (args.length != 3) {
			System.out.println();
			System.out.println("Usage: " + myClass.getClass().getSimpleName() + " <DNS-Namespace> <Username> <Password>\n");
			System.out.println("  where <DNS-Namespace> is the fully qualified DNS name of the HCP Namespace.");
			System.out.println("              For example: \"ns1.ten1.myhcp.example.com\"");
			System.out.println("        <Username> and <Password> are the credentials of the HCP user with data");
			System.out.println("              access permissions for the namespace.");
			System.out.println();
			
			System.exit(-1);
		}
		
		try {
			// Initialize the class with the input parameters.
			myClass.initialize(args[0], args[1], args[2]);

			// Write object to HCP
			myClass.WriteToHCP();
	
			// Read object from HCP
			myClass.ReadFromHCP();
			
			// Cleanup before object destruction.
			myClass.shutdown();

		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
